﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Windows.Forms;

namespace passitumi
{
	/**
	 * This class is responsible for drawing the main GUI window as well as forming
	 * the responses to the events of peer addition and removal. The GUI intialization
	 * will also start up the ServiceLocator, ServiceBroadcaster and FileListener componets
	 * which give the application functionality.
	 */
	public partial class Gui : Form
	{
		// Arraylists to contain the current peers that are active
		private ArrayList peerInfo = new ArrayList();
		private ArrayList peerButtons = new ArrayList();

		// GUI window components
		private System.Windows.Forms.Panel panList;
		private System.Windows.Forms.FlowLayoutPanel peerList;
		private System.Windows.Forms.Label lblHostInfo;
		private System.Windows.Forms.Button butEditPrefs;

		// The service locator and broadcaster objects as well as threads for them
		private ServiceBroadcaster broadcaster;
		private ServiceLocator locator;

		// Event handlers for when peers are lost and discovered
		public delegate void NewPeerDetected(string displayName);
		public delegate void PeerLost(string machineName);

		// Event handler for when preferences have been changed
		public delegate void PreferencesChanged(string displayName);

		/**
		 * Constructor which will initialize the GUI as well as start up the
		 * service-discovery feature and the TCP listener.
		 */
		public Gui()
		{
			Console.WriteLine("Gui: Running [{0}]", System.Threading.Thread.CurrentThread.ManagedThreadId);
			
			// Create the GUI
			InitializeComponent();
			fillHostInfo();

			// Run the local method to intialize all of the Mono.Zeroconf service discovery utilities
			initializeServiceDiscovery();

			// Initialize listener for TCP connections and file transfers
			//initializeFileListener();

			this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.close);
		}

		/**
		 * A method to figure out the host information and to display it in the
		 * label at the bottome of the application's GUI.
		 */
		private void fillHostInfo()
		{
			//passitumi.Properties.Settings.Default.displayName;

			// Figure out the host's name and IP addresses
			string hostName = Dns.GetHostName();
			IPHostEntry ipEntry = Dns.GetHostByName(hostName);
			IPAddress[] addr = ipEntry.AddressList;

			// Print the host's information in the label
			lblHostInfo.Text = "Name: " + passitumi.Properties.Settings.Default.displayName;
			for (int i = 0; i < addr.Length; i++)
			{
				lblHostInfo.Text = lblHostInfo.Text + Environment.NewLine + "IP Address(" + (i + 1) + ") : " + addr[i].ToString();
			}
		}

		#region Windows Form Designer generated code

		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Gui));
			this.panList = new System.Windows.Forms.Panel();
			this.peerList = new System.Windows.Forms.FlowLayoutPanel();
			this.lblHostInfo = new System.Windows.Forms.Label();
			this.butEditPrefs = new System.Windows.Forms.Button();
			this.panList.SuspendLayout();
			this.SuspendLayout();
			// 
			// panList
			// 
			this.panList.AutoScroll = true;
			this.panList.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
			this.panList.Controls.Add(this.peerList);
			this.panList.Location = new System.Drawing.Point(12, 12);
			this.panList.Name = "panList";
			this.panList.Size = new System.Drawing.Size(220, 485);
			this.panList.TabIndex = 1;
			// 
			// peerList
			// 
			this.peerList.AutoSize = true;
			this.peerList.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
			this.peerList.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
			this.peerList.Location = new System.Drawing.Point(3, 3);
			this.peerList.Name = "peerList";
			this.peerList.Size = new System.Drawing.Size(0, 0);
			this.peerList.TabIndex = 0;
			// 
			// lblHostInfo
			// 
			this.lblHostInfo.Location = new System.Drawing.Point(12, 504);
			this.lblHostInfo.Name = "lblHostInfo";
			this.lblHostInfo.Size = new System.Drawing.Size(161, 53);
			this.lblHostInfo.TabIndex = 2;
			this.lblHostInfo.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
			// 
			// butEditPrefs
			// 
			this.butEditPrefs.Image = ((System.Drawing.Image)(resources.GetObject("button1.Image")));
			this.butEditPrefs.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
			this.butEditPrefs.Location = new System.Drawing.Point(179, 517);
			this.butEditPrefs.Name = "butEditPrefs";
			this.butEditPrefs.Size = new System.Drawing.Size(53, 27);
			this.butEditPrefs.TabIndex = 3;
			this.butEditPrefs.Text = "Edit";
			this.butEditPrefs.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
			this.butEditPrefs.UseVisualStyleBackColor = true;
			this.butEditPrefs.Click += new System.EventHandler(this.butEditPrefs_Click);
			// 
			// Gui
			// 
			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
			this.ClientSize = new System.Drawing.Size(242, 566);
			this.Controls.Add(this.butEditPrefs);
			this.Controls.Add(this.lblHostInfo);
			this.Controls.Add(this.panList);
			this.Name = "Gui";
			this.Text = "Passitumi";
			this.panList.ResumeLayout(false);
			this.panList.PerformLayout();
			this.ResumeLayout(false);

		}
		#endregion

		/**
		 * This method is the event handler for the ServiceLocator events. When an event
		 * is passed to the GUI, this method will intercept it and determine the type of event
		 * and service it. The event itself will contain the information about the event in the
		 * form of the args.
		 */
		public void locatorEventReceived(object o, ServiceEventArgs args)
		{
			// Depending on the event type the event will be serviced appropriatly
			switch (args.eventType)
			{
				case ServiceEventArgs.PEER_FOUND:
					// A new peer has been found, invoke the method to add a new peer button through the main thread
					Invoke(new NewPeerDetected(addNewPeerButton), new object[] { args.displayName });
					break;
				case ServiceEventArgs.PEER_REMOVED:
					// A peer has been removed, invoke the method to remove a peer button through the main thread
					Invoke(new PeerLost(removePeerButton), new object[] { args.displayName });
					break;
				case ServiceEventArgs.ERROR:
				default:
					// Error, or bad event has been received
					Console.WriteLine("Gui: locatorEventReceived - ERROR (Bad event received from ServiceLocator)");
					break;
			}
		}

		/**
		 * When the 'Exit' button is clicked the running threads will terminate
		 * and the application will shutdown, any final checks can be performed here.
		 */

		protected void close(object sender, FormClosingEventArgs e)
		{
			Console.WriteLine("Gui: Closing");

			// Shutdown the application
			ServiceLocator.getInstance().stop();
			ServiceBroadcaster.getInstance().stop();
		}

		/*
		 * addNewPeerButton(string machineName, string ipAddress, short port)
		 * 
		 * Add a new button to the GUI that represents a peer.
		 * This method is usually called when the Gui has received
		 * an event from ServiceLocator that a new peer has been found.
		 */
		/**
		 * This method will activate when a new peer is found and is a result of the 
		 * new peer event, a new peer is added to the GUI. The peer will be added as a
		 * new button which will initiate a file transfer dialog when clicked. The 
		 * arraylists will also be updated with the new peer information as well.
		 */
		public void addNewPeerButton(string displayName)
		{
			// Create a new button that will represent the new peer
			Button newPeerButton = new Button();
			newPeerButton.Width = 200;
			newPeerButton.TextAlign = System.Drawing.ContentAlignment.TopLeft;

			// Text for the button is comprised of the display name of the peer
			newPeerButton.Text = "Name: " + displayName;

			// Create an event listener for when the peer's button is clicked
			newPeerButton.Click += delegate(object sender, EventArgs e)
			{
				{
					// Establish the button that is being used
					Button button = sender as Button;

					/*
						// Create a file chooser dialog
						OpenFileDialog chooser = new OpenFileDialog();
						chooser.RestoreDirectory = true;

						// Show the file chooser dialog and if the user sends a file execute the file transmiting procedure
						if (chooser.ShowDialog() == DialogResult.OK)
						{
							// Acquire the file name that the user choose to send
							string fName = chooser.FileName.Substring(chooser.FileName.LastIndexOf(System.IO.Path.DirectorySeparatorChar));

							// Creates the socket connection (http://codetechnic.blogspot.com/2009/02/sending-large-files-over-tcpip.html)
							System.Net.Sockets.Socket clientSock = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
							clientSock.SendBufferSize = 8192; // Buffer size of 8KBs, could use adjusting to maximize performance

							byte[] fileName = Encoding.UTF8.GetBytes(fName); // Acquire the file name
							byte[] fileData = File.ReadAllBytes(chooser.FileName); // Acquire the data in the file
							byte[] fileNameLen = BitConverter.GetBytes(fileName.Length); // Acquire the lenght of file name

							// A byte array which will hold the client data to be sent
							byte[] clientData = new byte[4 + fileName.Length + fileData.Length];
							fileNameLen.CopyTo(clientData, 0);
							fileName.CopyTo(clientData, 4);
							fileData.CopyTo(clientData, 4 + fileName.Length);

							// Form a connection to the target using the socket connection
							clientSock.Connect(ipAddress, 3691);

							// Send the data then close the socket connection
							clientSock.Send(clientData);
							clientSock.Close();
						}*/
				};
			};

			// Add the newly added peer to the arraylists used to manage the peers
			this.peerButtons.Add(newPeerButton);
			this.peerInfo.Add(displayName);

			// Add the new button to the peerList layoutpanel and update it
			peerList.Controls.Add(newPeerButton);
			peerList.Update();
		}

		/**
		 * This method will activate when the peer removal event occures, which will
		 * remove the first peer that matches in regards to the peer name. The peer
		 * will be removed from the internal arraylists as well as the the button itself. 
		 */
		public void removePeerButton(string serviceName)
		{
			int index = -1;

			// Loop through each of the active peers
			foreach (string peerName in peerInfo)
			{
				index++;

				// Check to see if the current peer matches with the one to be removed
				if (peerInfo[index].Equals(peerName))
				{
					// Acquire the button for the peer to be removed and remove it
					Button but = (Button)peerButtons[index];
					peerList.Controls.Remove(but);
					peerList.Update();
					break;
				}
			}

			// Remove the peer from the internal arraylists
			peerInfo.RemoveAt(index);
			peerButtons.RemoveAt(index);
		}

		/**
		 * This method will create the ServiceBroadcaster and ServiceLocator
		 * objects which will broadcast the service and browse for similar 
		 * applications.
		 */
		private void initializeServiceDiscovery()
		{
			// The broadcaster which makes this client available on the network
			this.broadcaster = ServiceBroadcaster.getInstance();
			this.broadcaster.start();

			// The locator which finds other services on the network
			this.locator = ServiceLocator.getInstance();

			// Add an event handler for when the locator has an event, so we know to update the GUI
			locator.OnServiceLocatorEvent += new ServiceLocator.ServiceLocatorEventHandler(locatorEventReceived);

			this.locator.start();
		}

		/**
		 * This method will initialize the FileListener object which
		 * will keep a TCP socket open for incoming TCP file transfers.
		 */
		private void initializeFileListener()
		{
			// Start up the file listener to listen for files being sent to the host
			//new FileListener(passitumi.Properties.Settings.Default.downloadFilePath);
		}

		/**
		 * The event of when the butEditPrefs button is clicked, this will
		 * open the preference dialog where the user can make changes to the settings.
		 */
		private void butEditPrefs_Click(object sender, EventArgs e)
		{
			openPreferencesWindow();
		}

		/*
		 * Open the preferences window where the user can alter their local
		 * application settings.
		 */
		public void openPreferencesWindow()
		{
			PreferencesGui prefs = new PreferencesGui();
			prefs.ShowDialog();

			// Update the main gui info for our local host
			fillHostInfo();
		}
	}
}